/* Copyright (c) 2016 Qualcomm Technologies International, Ltd. */
/*   %%version */

#include "panic/panic_private.h"

#include "hal/hal.h"


/**
 * Protest volubly and die
 *
 * IMPLEMENTATION NOTE
 *
 * Ideally this function would only perform trivial operations that do not
 * rely on any memory contents or other state. This is because it is likely
 * to be called when something has gone disasterously wrong, in which case
 * the state cannot be assumed to be valid. Functions from the HAL can be
 * considered reasonably safe, but other functions should generally be
 * avoided.
 *
 * Unfortunately compromises need to be made. These exceptions should be kept
 * to a minimum.
 */
void panic_diatribe(panicid deathbed_confession, DIATRIBE_TYPE diatribe)
{
    TIME panic_time;

    /* Block interrupts (but not exceptions) to ensure state is accurately preserved */
    block_interrupts();
    panic_time = hal_get_time();

    L0_DBG_MSG2("PANIC 0x%x %d", deathbed_confession, diatribe);

    {
        /* On P1 report down to P0; on P0 report down to Curator.
         *
         * IN EITHER CASE WE CANNOT GUARANTEE HOW MUCH MORE CODE WE WILL EXECUTE
         * BEFORE BEING HALTED BY WHOWEVER WE REPORTED TO.
         */
        panic_report_down(deathbed_confession, panic_time, diatribe);
    }

#ifdef PIO_DEBUG_MODULE_PRESENT
    /* Indicate the reason code on the LEDs and/or PIOs if required */
    PIO_TRACE_POINT(PANIC);
#endif


    /*
     * On a standalone chip we would reset or halt the processor as
     * appropriate.  Here, we wait for the Curator to take action
     * based on the subreport message.
     */

    /* On an embedded platform this enters an infinite loop (at least until
       the chip is reset, e.g. by the watchdog if enabled) */
#if defined(DESKTOP_TEST_BUILD)
    /*NOTREACHED*/
    #include <stdlib.h>
    exit((int) deathbed_confession);
#else
    /* We want to end up in an infinite loop here rather than in the CRT (which
     * is how exit is implemented) so that we remain in DWARF-instrumented code
     * and can get the all-important panic backtrace. */
    for (;;)
    {
    } /* Note: if you write the obvious "for (;;);" lint whinges */
#endif /* DESKTOP_TEST_BUILD */
}

/**
 * Protest pathetically and die
 */
void panic(panicid deathbed_confession)
{
    panic_diatribe(deathbed_confession, 0U);
}

/** 
 * Callback function for panic_diatribe_later() timed event
 */
#ifdef __KALIMBA__
#if !defined(__GNUC__)
/*lint -e975 Don't worry about the unrecognised pragma, lint */
#pragma ckf panic_diatribe_later_fn f DoesNotReturn
#endif
#endif
static void panic_diatribe_later_fn(uint16 deathbed_confession, void *diatribe)
{
    panic_diatribe((panicid)deathbed_confession, POINTER_CAST_FOR_DIATRIBE(diatribe));
}

/** 
 * Schedule panic_diatribe() after delay
 */
void panic_diatribe_later(panicid deathbead_confession, 
                          DIATRIBE_TYPE diatribe, 
                          int32 delay)
{
    (void)timed_event_in(delay, panic_diatribe_later_fn,
                         deathbead_confession, (void *)diatribe);
}

